(ns rete
  (:require [clojure.xml :as xml])
  (:require [clojure.zip :as zip])
  (:require [clojure.set :as s]) 
  (:require [clojure.contrib.zip-filter.xml :as zf]))

;::::: DEFINICIONES :::::::


;(def records (zip/xml-zip xml-doc))
(def reteNet (ref {})) ;Map principal, contiene el acceso a la red
(def actualLeftHandSide (ref {})) ;; Condiciones
(def actualRightHandSide (ref{})) ;; Conclusiones
(def workingMemory (ref #{}))

;::::: FUNCIONES :::::::

(defn loadNode [_node _id _key value]
  (. _node setName _id)
  (. _node setKey _key)
  (. _node setValue value)
  )

;-------------------------------------------------------------------------------------

(defn createBethaMemory [collLHS collRHS]  
  (def lastBNode (common.BethaNode.))  
  (doseq [lhs (seq collLHS)]   
    ;Construyo las memorias Beta, como supongo que no pueden haber variables en 
    ; una regla. Las voy conectando de acuerdo al orden que tienen en el Map    
    (if (< (. lastBNode getParentSize) 2)      
      (. (first (second lhs)) linkBethaMemory lastBNode)   
      (do        
        (def lastWBNode (. lastBNode getWorkingBethaNode))
        (def lastBNode (common.BethaNode.))
        (. lastWBNode addBethaNode lastBNode)
        (. (first (second lhs)) linkBethaMemory lastBNode)
       )
     )    
  )
  (let [lastWBNode (. lastBNode getWorkingBethaNode)]
  (doseq [rhs (seq collRHS)]
    ;Agrego las Conclusiones de la Regla al WorkingBethaNode que sea Hoja
    (. lastWBNode addRHS (second rhs))
    )
  )
)

;-------------------------------------------------------------------------------------

(defn printRete [coll]
  (doseq [x (seq coll)]
    (doseq [y (seq (second x))]
      (println  y)
      )
    )
  )

;-------------------------------------------------------------------------------------

(defn getNode [coll nodo]
  (def nodeAux (ref nil))
  (doseq [x coll]
    (class (. x equals nodo))
    (if (. x equals nodo)
      (dosync (ref-set nodeAux x))
      )
    )
  @nodeAux  
  )

;-------------------------------------------------------------------------------------

(defn addLHS [_node]
  "Deja la condicion en @actualLeftHandSide, chequea contra @reteNet"     
  (let [alpha _node
        _key (. alpha getKey)
        _value (. alpha getValue)
        _id (. alpha getName)
        ]       
    (println (str "Agregando: " _id "\\" _key "=" _value))
    (if (re-find #"^[0-9]+$" _value) ;Si la regla tiene seteado un numero
      
      ;(loadNode alpha _id _key _value)	              
      (dosync
        (if (contains? @reteNet _key)
          ;Agrego al Set el AlphaNode actual, ya que se encontro una entrada en el map
          (let [actualSet (get @reteNet _key)
                actualNode (getNode actualSet alpha)]          
            ;;si la condicion esta en el actualSet => devuelvo la misma instancia
            ;;sino hago lo que sigue 
            ;(println "Existe la entrada")
            (if-not (nil? actualNode)   
           ;   (
                (def finalSet actualSet)  ;Devuelvo la instancia ya cargada
                ;(println "aca estoy")
                ;(println finalSet)
               ; )
              (do
                (def finalSet (conj actualSet alpha))
               ; (println "Nuevo Nodo")  
                ;(println finalSet)
                )
              )
            
            (ref-set actualLeftHandSide (merge (deref actualLeftHandSide) {_key finalSet}))    
            ;(println @actualLeftHandSide)
            )                    
          (do  ;Agrego un clave=valor nuevo
            
            (let [newMap {_key #{alpha}}]
              (ref-set actualLeftHandSide (merge (deref actualLeftHandSide) newMap))
              ;(println "No existe la entrada")
              )
            )
          )
        )
      )
    ) 
  ;(println @reteNet)
  ;(println "-----------")
 )

;-------------------------------------------------------------------------------------

(defn addRHS [_id _key _value]
  (let [nodo (common.Node.)]
    (loadNode nodo _id _key _value)
    (dosync (ref-set actualRightHandSide (merge (deref actualRightHandSide) {_key nodo})))
    )
  )

;-------------------------------------------------------------------------------------
      
(defn createRete [xml]
  (dosync ( ref-set reteNet (hash-map)))
  
  (doseq [x (xml-seq xml) :when (= :rule (:tag x))]  ;<rule>
    (dosync (ref-set actualRightHandSide {})
      (doseq [y (:content x) :when (= :then (:tag y))]  ;<then>
        (doseq [z (:content y) :when (= :attr (:tag z))]  ;<attr>      
          (addRHS (:id (:attrs x)) (:name (:attrs z)) (first (:content z))) ;;Right-Hand Side
          )
        )

      (dosync (ref-set actualLeftHandSide {})
        (doseq [y (:content x) :when (= :if (:tag y))]  ;<if>          
          (def ifSet #{})
          (def miMap {})
          (doseq [z (:content y) :when (= :attr (:tag z))]  ;<attr>
            (def alphaNode (common.AlphaNode.))
              (loadNode alphaNode (:id (:attrs x)) (:name (:attrs z)) (first (:content z)))                
                (addLHS alphaNode)  ;;Left-hand Side          
                
            )           
           
          
           (createBethaMemory @actualLeftHandSide @actualRightHandSide)          
            ;(println "--------")
            ;(println reteNet)
            (ref-set reteNet (merge @reteNet (deref actualLeftHandSide)))  ;deref y @ hacen lo mismo
            ;(println reteNet)
            ;(println "--------")
            )
          )
        )
      ; (println @reteNet)
      )  
    )

;-------------------------------------------------------------------------------------

(defn updateRete [alphaNode value]
  (let [workingAlpha (. alphaNode getWorkingAlphaNode)]
    (. alphaNode updateState value)    
    )
  )

;-------------------------------------------------------------------------------------
;TODO mejorar la funcion recursiva utilizando RECUR, en vez del doseq ver de usar some
(defn addFact[nodo]
  (dosync
    (if-not (some #(= nodo %) @workingMemory)
      (do 
        (ref-set workingMemory (conj @workingMemory nodo))        
        (let [alphaSet (get @reteNet (. nodo getKey))]
          (if-not (nil? alphaSet)            
            (doseq [x alphaSet]
              (if (. x equals nodo)
                (let [listaRHS (. x updateState (. nodo getValue))]
                  (doseq [nodoRHS listaRHS]                           
                    
                    ;(def coll (clojure.set/union coll nodoRHS))              
                    (addFact nodoRHS) 
                    )
                  )
                )          
              )
            )
          )   
        )
      )
    )
  )

;-------------------------------------------------------------------------------------












 


